/*
 * hash_map.h
 *
 *  Created on: 2012-8-5
 *      Author: lin
 */

#ifndef HASH_MAP_H_
#define HASH_MAP_H_

#include "def/defs.h"
#include "zmalloc/zmalloc.h"

#define DEF_HASH_MAP_CAPACITY     8
#define DEF_HASH_MAP_FACTOR 0.75

#define HASH_MAP_OK 0
#define HASH_MAP_ERR 1
#define HASH_MAP_ABSENT 2
#define HASH_MAP_EXIST 3

typedef struct hash_map_entry {
    mixed_t key;
    mixed_t val;
    struct hash_map_entry *next;
    uint32_t hash;
} hash_map_entry_t;

typedef struct {
    mixed_t_hash_fn key_hash_fn;
    mixed_t_dup_fn key_dup_fn;
    mixed_t_dup_fn val_dup_fn;
    mixed_t_compare_fn key_compare_fn;
    mixed_t_free_fn key_free_fn;
    mixed_t_free_fn val_free_fn;

    hash_map_entry_t **table;

    uint32_t capacity;
    uint32_t capacity_mask;
    uint32_t size;
    uint32_t thresold;
    float factor;

} hash_map_t;

hash_map_t* hash_map_new0(uint32_t capacity, float factor);

static inline hash_map_t* hash_map_new(void) {
    return hash_map_new0(DEF_HASH_MAP_CAPACITY, DEF_HASH_MAP_FACTOR);
}
void hash_map_clean(hash_map_t* hmap);

static inline void hash_map_free(hash_map_t* hmap) {
    hash_map_clean(hmap);
    zfree(hmap);
}

void hash_map_rehash(hash_map_t* hmap, uint32_t newcapacity);

int hash_map_put_absent(hash_map_t* hmap, mixed_t key, mixed_t val);

static inline int hash_map_put_absent_ptr(hash_map_t *hmap, void *key,
        void *val) {
    mixed_t mk, mv;
    mk.ptr = key;
    mv.ptr = val;
    return hash_map_put_absent(hmap, mk, mv);
}
int hash_map_put(hash_map_t* hmap, mixed_t key, mixed_t val);

static inline int hash_map_put_ptr(hash_map_t *hmap, void *key, void *val) {
    mixed_t mk, mv;
    mk.ptr = key;
    mv.ptr = val;
    return hash_map_put(hmap, mk, mv);
}

int hash_map_remove(hash_map_t* hmap, mixed_t key);
int hash_map_remove_nofree(hash_map_t* hmap, mixed_t key);

int hash_map_get_hash(hash_map_t *hmap, mixed_key, uint32_t keyhash,
        mixed_t *val);

static inline int hash_map_get(hash_map_t* hmap, mixed_t key, mixed_t* val) {
    uint32_t hash = hmap->key_hash_fn(key);
    return hash_map_get_hash(hmap, key, hash, val);
}

static inline void *hash_map_get_ptr_hash(hash_map_t *hmap, void *key,
        uint32_t keyhash) {
    mixed_t k, v;
    k.ptr = key;
    if (hash_map_get_hash(hmap, k, keyhash, &v) == HASH_MAP_EXIST) {
        return v.ptr;
    }
    return NULL;
}
static inline void* hash_map_get_ptr(hash_map_t *hmap, void*key) {
    uint32_t hash = hmap->key_hash_fn(key);
    return hash_map_get_ptr_hash(hmap,key,hash);
}
hash_map_entry_t* hash_map_begin(hash_map_t *hmap);
hash_map_entry_t* hash_map_next(hash_map_t *hmap, hash_map_entry_t* cur);

#define hash_map_foreach(hmap, entry) \
for(entry = hash_map_begin(hmap); entry; entry = hash_map_next(hmap, entry))

#define hash_map_size(hmap) (hmap)->size
#define hash_map_is_empty(hmap) (!(hmap)->size)
#define hash_map_capacity(hmap) (hmap)->capacity

#define hash_map_set_hash_fn(hmap,fn) (hmap)->key_hash_fn = (fn)
#define hash_map_set_dup_key_fn(hmap,fn) (hmap)->key_dup_fn = (fn)
#define hash_map_set_dup_val_fn(hmap,fn) (hmap)->val_dup_fn = (fn)
#define hash_map_set_comp_key_fn(hmap,fn) (hmap)->key_compare_fn = (fn)
#define hash_map_set_free_key_fn(hmap,fn) (hmap)->key_free_fn = (fn)
#define hash_map_set_free_val_fn(hmap,fn) (hmap)->val_free_fn = (fn)

#endif /* HASH_MAP_H_ */
